<template lang="html">
  <div class="player__wrapper">
    <div class="player__progress">
      <div class="time">
        <span class="time-now">{{currentTimeFmt}}</span>
        <span class="time-all">-{{leftTimeFmt}}</span>
      </div>
      <div class="player__progress-bar" ref="progressBar">
        <v-slider
          :style="{width: '100%'}"
          v-model="progress"
          :bufferValue="bufferedProgress"
          :bufferTrans="bufferedProgressTrans"
          runwayColor="rgba(255,255,255,0.3)"
          barColor="#fff"
          buttonColor="#fff"
          :isloading="isloading"
          @change="changeProgress">
        </v-slider>
      </div>
    </div>
    <div class="player__btn-group" ref="playerBtnGroup">
      <div
        class="prev"
        :class="[isFirstAudio ? 'disabled' : '']"
        @click="prev">
      </div>
      <div
        ref="playBtn"
        :class="[canPlayArr[currentIndex] ? '' : 'disabled', playing ? 'pause' : 'play']"
        @click="playOrPause(currentIndex)">
      </div>
      <div
        class="next"
        :class="[isLastAudio ? 'disabled' : '']"
        @click="next">
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import VSlider from './slider/index'
import { fmtSecToMin } from './common/util'
import {
  getNetworkStatus,
  playAudioByNative,
  pauseAudioByNative,
  stopAudioByNative,
  getAudioProgress
} from '../../api'
import { eventTrack } from '../../util/decorator'
import Howl from 'howler'
const HowlAny:any = Howl.Howl

var ua = navigator.userAgent;
  // TODO 精确性待改进
var ANDROID = /android/i.test(ua);
var IOS = /iphone|ipad/i.test(ua);
var WP = /windows phone/i.test(ua);


interface dataModel {
  soundArr: any;
  canPlayArr: boolean[];
  loaded: boolean;
  loadedIndex: number;
  firstLoad: boolean;
  playing: boolean;
  bufferedProgress: number;
  progress: number;
  totalTime: number;
  currentTime: number;
  step: number;
  progressInterval: any;
  bufferInterval: any;
  isloading: boolean;
  currentIndex: number;
  bufferedProgressTrans: boolean;
  soundCacheList: any;
}

export default Vue.extend({
  name: 'Player',

  data () { 
    const initialState: dataModel = {
      soundArr: {},
      canPlayArr: [],
      loaded: false,
      loadedIndex: 0,
      firstLoad: true,
      playing: false,
      bufferedProgress: 0,
      progress: 0,
      totalTime: 0,
      currentTime: 0,
      step: 0,
      progressInterval: {},
      bufferInterval: {},
      isloading: true,
      currentIndex: 0,
      bufferedProgressTrans: false,
      soundCacheList: []
    }
    return initialState
  },

  props: {
    isFirstAudio: {
      type: Boolean,
      default: true
    },
    isLastAudio: {
      type: Boolean,
      default: false
    },
    audioSrcArr: {
      type: Array,
      default: function () {
        return []
      }
    }
  },

  computed: {
    currentTimeFmt ():string {
      return fmtSecToMin(this.currentTime)
    },

    leftTime ():number {
      return this.totalTime - this.currentTime
    },

    leftTimeFmt ():string {
      return fmtSecToMin(this.leftTime)
    }
  },

  watch: {
    currentTime (val:number) {
      this.progress = this.currentTime / this.totalTime * 100
    },

    audioSrcArr (val, oldVal) {
      if(val.length > 0 && this.firstLoad) {
        this.init()
        return
      }

      //根据新旧值比较来判断audioSrcArr是新增还是重组，来执行不同的初始化方式，避免新增数据使得canPlayArr和soundArr原来的数据丢失。
      let lenDiff = val.length - oldVal.length
      if(lenDiff > 0) {
        let arrDiff = val.slice(0, oldVal.length)
        let oldValArr = oldVal.slice(0, oldVal.length)
        let diff = true
        //无法直接比较两个数组，返回一直是false，只能用遍历，i dont know why _(:3」∠)_
        oldValArr.map((item:string, index:number) => {
          if(item !== arrDiff[index]) {
            diff = false
            return
          }
        })
        if(diff) {
          this.changeAudioSrcArr('add', lenDiff)
        } else {
          this.changeAudioSrcArr('recompose', lenDiff)
        }
      }
    }
  },

  components: {
    VSlider
  },

  destroyed () {
    //TODO 清除缓存

  },

  methods: {
    init () {
      this.canPlayArr = Array(this.audioSrcArr.length).fill(false)
      this.soundArr = Array(this.audioSrcArr.length).fill({})
      this.initSound(0)
      // this.initAllSound() //一次性加载所有音频 =(☉㊀☉)つ
    },

    initAllSound () {
      const _self = this
      this.initSoundPromise(this.loadedIndex)
    },

    initSoundPromise (index:number) {
      const _self = this
      let sound = new HowlAny({
        src: _self.audioSrcArr[index],
        format: ['mp3'],
        html5: true,
        preload: true,
        onload: function () {
          if(index === _self.currentIndex) {
            _self.play(index)
            _self.isloading = false
            _self.finishBufferProgress(false)
            if(_self.firstLoad) {
              if(index === 0) {
                _self.totalTime = this._duration
              }
            }
          }
          _self.$set(_self.canPlayArr, index, true)
          _self.soundArr[index] = this
          if(index < _self.audioSrcArr.length - 1) {
            _self.loadedIndex ++
            _self.initSoundPromise(_self.loadedIndex)
          }
        }
      })
    },

    /**
    * 加载单独的音频
    * @param index 音频地址在数组中的索引值
    **/
    initSound (index:number) {
      getNetworkStatus().then(resp => {
        if (resp.data === '0') {
          this.$toast('无法连接网络')
        } else if (resp.data === '2' && !sessionStorage.getItem('fm_celluar_load')) {
          this.$messageBox.confirm('音频文件的播放会消耗您的流量，建议在wifi下打开，是否继续播放').then(() => {
            sessionStorage.setItem('fm_celluar_load', '1')
            this.loadAudio(index)
          })
        } else {
          this.loadAudio(index)
        }
      }).catch(resp => {
        this.$toast(resp.data)
      })
    },

    loadAudio(index: number): any {
      this.$emit('loadCompleted', false)
      const _self = this
      this.isloading = true
      let sound =  new HowlAny({
        src: [_self.audioSrcArr[index]],
        format: ['mp3'],
        html5: true,
        preload: true,
        onload: function () {
          _self.isloading = false
          _self.$set(_self.canPlayArr, index, true)
          _self.finishBufferProgress(false)
          _self.totalTime = this._duration
          _self.$emit('loadCompleted', true)
          if(_self.firstLoad) {
            _self.firstLoad = false
          } else {
            if(index === _self.currentIndex) {
              _self.changePlay(index)
            }
          }
        }
      })
      this.soundArr[index] = sound
      //增加音频缓存数组，默认5条，超过5条清除最早的一条音频。限制同时间音频加载的数量，保证单条音频的加载速度
      this.soundCacheList.push(index)
      if(this.soundCacheList.length > 5) {
        let lastCacheIndex = this.soundCacheList[0]
        this.soundArr[lastCacheIndex].unload()
        this.soundArr[lastCacheIndex] = {}
        this.soundCacheList.shift()
      }
    },

    /**
    * 判断改变音频地址数组的方式（新增or重组）
    * @param type 改变数组的方式（新增：add，重组：recompose）
    * @param lenDiff 新旧数组长度差
    **/
    changeAudioSrcArr (type:string, lenDiff:number) {
      if(type === 'add') {
        let canPlayArrAdd = Array(lenDiff).fill(false)
        this.canPlayArr = [...this.canPlayArr, ...canPlayArrAdd]
        let soundArrAdd = Array(lenDiff).fill({})
        this.soundArr = [...this.soundArr, ...soundArrAdd]
      } else if (type === 'recompose') {
        this.init()
      }
    },

    /**
    * 切换音频
    * @param index 音频地址在数组中的索引值
    **/
    changeAudio (index:number) {
      this.currentIndex = index
      this.reset()
      //如果该音频已初始化，执行播放。反之执行初始化
      if(this.soundArr[index] instanceof HowlAny) {
        this.changePlay(index)
      } else {
        this.initSound(index)
      }
    },

    /**
    * 切换音频的播放方法
    * @param index 音频地址在数组中的索引值
    **/
    changePlay (index:number) {
      this.bufferedProgress = 100
      this.currentIndex = index
      this.totalTime = this.soundArr[index]._duration
      this.play(index)
    },

    /**
    * 判断音频播放状态，执行播放还是暂停
    * @param index 音频地址在数组中的索引值
    **/
    playOrPause (index:number) {
      eventTrack('007003');
      if(this.playing) {
        this.pause(index)
      } else {
        this.play(index)
      }
    },

    play (index:number) {
      playAudioByNative(this.audioSrcArr[index]).then(() => {
        this.playing = true
        this.isloading = false
        this.$emit('loadCompleted', true)
        this.setProgressItv()
      })
      // if(this.soundArr[index].play) {
      //   if(this.canPlayArr[index]) {
      //     this.$emit('loadCompleted', true)
      //     this.isloading = false
      //     this.playing = true
      //     this.soundArr[index].play()
      //     this.setProgressItv()
      //   } else {
      //     this.$emit('loadCompleted', false)
      //     this.isloading = true
      //   }
      // }
    },

    pause (index:number) {
      // if(this.soundArr[index]) {
      this.$toast(this.audioSrcArr[index])
      pauseAudioByNative(this.audioSrcArr[index]).then(() => {
        this.clearProgressItv()
        this.playing = false
      })
      // }
    },

    stop (index:number) {
      stopAudioByNative(this.audioSrcArr[index]).then(() => {
        this.playing = false
      })
      // if((this.soundArr[index] instanceof HowlAny)) {
      //   this.soundArr[index].stop()
      //   this.playing = false
      // }
    },

    prev () {
      eventTrack('007002')
      if(this.currentIndex > 0) {
        this.currentIndex --
        this.changeAudio(this.currentIndex)
        if(!this.isFirstAudio) {
          this.$emit('prev')
        }
      }
    },

    next () {
      eventTrack('007004')
      if(this.currentIndex < this.soundArr.length - 1) {
        this.currentIndex ++
        this.changeAudio(this.currentIndex)
        if(!this.isLastAudio) {
          this.$emit('next')
        }
      }
    },

    /**
    * 重置播放器播放状态
    **/
    reset () {
      this.clearProgressItv()
      if(this.soundArr.length > 0) {
        this.soundArr.map((item:any) => {
          if(item.stop) {
            if(item.playing()) {
              item.stop()
            }
          }
        })
        this.playing = false
        this.isloading = false
        this.currentTime = 0
        this.progress = 0
        this.bufferedProgress = 0
        this.stop(this.currentIndex)
      }
    },

    /**
    * 改变播放进度条的定时器,间隔1s
    **/
    setProgressItv () {
      this.progressInterval = setInterval(() => {
        getAudioProgress().then(resp => {
          this.$toast(String(resp.data))
          let progress = resp.data
          this.progress = progress * 100
        })
      }, 1000)
      // this.progressInterval = setInterval(() => {
      //   if(this.currentTime < this.totalTime - 1) {
      //     if(this.soundArr[this.currentIndex].playing()) {
      //       this.currentTime ++
      //     }
      //   } else {
      //     this.reset()
      //   }
      // }, 1000)
    },

    clearProgressItv () {
      clearInterval(this.progressInterval)
    },

    /**
    * 改变当前音频播放进度
    * @param pecent 进度 [0~100]
    **/
    changeProgress (pecent:number) {
      eventTrack('007001')
      let pos = parseInt(String(this.totalTime * pecent / 100))
      this.currentTime = pos
      this.soundArr[this.currentIndex].seek(pos)
    },

    /**
    * 改变缓冲进度条长度
    * @param ifTrans 是否开启随机过渡效果
    **/
    finishBufferProgress (ifTrans:boolean) {
      if(ifTrans) {
        let des = Math.random() * 10
        this.changeBufferProgress(des, 0)
        .then(() => {
          des = (Math.random() * 40 + 10)
          this.changeBufferProgress(des, 1000)
          .then(() => {
            des = Math.random() * 30 + 50
            this.changeBufferProgress(des, 1000)
            .then(() => {
              this.changeBufferProgress(100, 1000)
            })
          })
        })
      } else {
        this.changeBufferProgress(100, 0)
      }
    },

    changeBufferProgress (destination:number, delay:number): Promise<any> {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          this.bufferedProgress = destination
          resolve(true)
        }, delay)
      })
    }
  }
})
</script>

<style scoped>
.player__wrapper {
  padding: 0px 5px 0 5px;
  background-color: transparent;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
}
.player__progress {
  width: 100%;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.player__progress .time {
  width: 100%;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  justify-content: space-between;
}
.player__progress .time-now,
.player__progress .time-all {
  color: #fff;
}
.player__progress-bar {
  width: 100%;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  justify-content: center;
}
.player__btn-group {
  position: absolute;
  top: 45px;
  left: 0;
  padding: 0 20%;
  z-index: 2200;
  box-sizing: border-box;
  /*margin-top: 10px;*/
  width: 100%;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  justify-content: space-between;
  align-items: center;
  transition: 0.3s all;
}
.player__btn-group .play,
.player__btn-group .pause {
  width: 30px;
  height: 30px;
}
.player__btn-group .play.disabled,
.player__btn-group .pause.disabled,
.player__btn-group .prev.disabled,
.player__btn-group .next.disabled {
  opacity: 0.6;
}
.player__btn-group .prev,
.player__btn-group .next {
  width: 25px;
  height: 25px;
}
.player__btn-group .prev {
  background-image: url('../../assets/images/icon_fm_qianyihsou@2x.png');
  background-size: contain;
  background-repeat: no-repeat;
}

.player__btn-group .next {
  transform: rotate(180deg);
  background-image: url('../../assets/images/icon_fm_qianyihsou@2x.png');
  background-size: contain;
  background-repeat: no-repeat;
}

.player__btn-group .play {
  background-image: url('../../assets/images/icon_fm_bofang@2x.png');
  background-size: contain;
  background-repeat: no-repeat;
}

.player__btn-group .pause {
  background-image: url('../../assets/images/icon_fm_zanting@2x.png');
  background-size: contain;
  background-repeat: no-repeat;
}
</style>
